package thread;

/**
 * 缩小同步范围可以在保证并发安全的前提下提高并发效率
 */
public class SyncDemo2 {
    public static void main(String[] args) {
//        Shop shop = new Shop();
//        //Thread(Runnable r,String name) 参数1:线程的任务，参数2:线程的名称
//        Thread t1 = new Thread(()->shop.buy(),"范传奇");
//        Thread t2 = new Thread(()->shop.buy(),"王克晶");

        Shop shop1 = new Shop();
        Shop shop2 = new Shop();
        Thread t1 = new Thread(()->shop1.buy(),"范传奇");
        Thread t2 = new Thread(()->shop2.buy(),"王克晶");

        t1.start();
        t2.start();
    }
}

class Shop{
    /*
        当在一个成员方法上使用synchronized时，那么同步监视器对象就是当前方法
        的所属对象this，并且不可选。
     */
//    public synchronized void buy(){
    public void buy(){
        try {
            Thread t = Thread.currentThread();
            System.out.println(t.getName()+":正在挑衣服");
            Thread.sleep(5000);
            /*
                使用同步块可以更精准的控制多个线程需要同步执行的代码片段
                但是使用时要自行指定同步监视器对象
                该对象的选取原则:
                1:必须是引用类型
                2:多个线程看到的必须是同一个对象

                下面代码中:
                this: 当前对象,这里是Shop的一个实例
                由于上面main方法中t1和t2调用的是同一个shop的buy方法，因此这里
                它们看到的就是同一个对象

                指定同步监视器对象时，除了必须满足的有效性之外，还应到考虑是否
                合适。
                所谓合适的锁对象应当在多个线程发生"抢"的情况下发挥同步效果，而
                不存在"抢"的情况下允许同时进行
                比如:
                t1和t2当它们调用不同的Shop对象的buy方法时，不存在"抢"的问题
                那么执行到下面代码时就应当可以同时进行。

                基本上同步监视器对象选取的应该是临界资源，意思是"抢谁就锁谁"
             */
            synchronized (this) {
//            synchronized ("hello"){
                System.out.println(t.getName() + ":正在试衣服");
                Thread.sleep(5000);
            }
            System.out.println(t.getName()+":买好了");
        } catch (InterruptedException e) {
        }
    }
}
